package com.zero.common.security.filter;

import com.zero.common.security.vo.LoginUser;
import com.zero.common.security.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * token过滤器 验证token有效性
 * OncePerRequestFilter 是 Spring Web 中的一个抽象类，它的作用是确保在每个 HTTP 请求中，过滤器只会被执行 一次。
 * 这个类是 Spring Security 中常见的过滤器基类之一，尤其适用于需要执行一次性任务的过滤器，比如身份验证、日志记录、权限校验等。

 * SpringSecurity 的安全认证 核心
 * 只需满足以下2个条件：
 * 1. SecurityContextHolder.getContext().getAuthentication();                      不为空
 * 2. UserDetails principal = (UserDetails) authenticationToken.getPrincipal();
 *    String username = principal.getUsername();                                   这个方法不抛异常(null也可以认证通过)

 * 比如：
 * 1. SecurityContextHolder.getContext().setAuthentication(null);       ->         认证失败(因为上下文为空)
 * 2. LoginUser lus = new LoginUser();
 *    lus.setUser(null);
 *    UsernamePasswordAuthenticationToken fake = new UsernamePasswordAuthenticationToken(lus, null, null);
 *    SecurityContextHolder.getContext().setAuthentication(fake);       ->         认证失败(因为 null.getUsername() 抛出异常)
 * 3. LoginUser lus = new LoginUser();
 *    lus.setUser(new SysUser());
 *    UsernamePasswordAuthenticationToken fake = new UsernamePasswordAuthenticationToken(lus, null, null);
 *    SecurityContextHolder.getContext().setAuthentication(fake);       ->         认证成功
 * 4. LoginUser lus = new LoginUser();                                  ->         认证成功
 *    SysUser sysUser = new SysUser();
 *    sysUser.setUserName("cxk");
 *    lus.setUser(sysUser);
 *    UsernamePasswordAuthenticationToken fake = new UsernamePasswordAuthenticationToken(lus, null, null);
 *    SecurityContextHolder.getContext().setAuthentication(fake);
 *    UserDetails principal = (UserDetails) fake.getPrincipal();
 *    System.out.println(principal.getUsername());                      ->         输出 `cxk` -> 结论成立
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        // 从请求头中 根据token 去缓存中 获取当前用户信息(解析token)
        LoginUser loginUser = tokenService.getLoginUser(request);
        // 用户信息存在 && 还没有上下文
        if (loginUser != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // token续期
            tokenService.verifyToken(loginUser);
            // new 一个上下文对象(当前用户信息)
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            // 为认证信息设置详细信息（通常是 HTTP 请求中的额外数据，如 IP 地址等）
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            // ⭐设置上下文⭐
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        // 继续调用过滤链，传递请求和响应
        chain.doFilter(request, response);
    }
}
